#include "globals.h"
#include "scan.h"
#define BUFLEN	256	

char tokenstr[MAXSTR + 1];
static char lineBuf[BUFLEN];
static int linepos = 0;
static int bufsize = 0;
static int EOF_flag = FALSE;

static char getChar(void);
static void ungetChar(void);
static tokentype reservedlookup(char* s);

typedef enum {
	START,INNUM,INID,DONE,INCOMMENT,IN1,IN2,IN3
}statetype;

static struct
{
	char *str;
	tokentype tok;
}reserved_words[ALLTOKEN]{
	{"if",IF},{"else",ELSE},{"int",INT},{"return",RETURN},{"void",VOID},
	{"while",WHILE},{"+",PLUS},{"-",MINUS},{"*",TIMES},{"/",OVER},{">",GT},
	{"<",LT},{">=",GTEQ},{"<=",LTEQ},{"==",EQ},{"!=",NEQ},{"=",ASSIGN},
	{";",SMIT},{ ",",COMMA },{ "(",LKUO },{ ")",RKUO },{ "[",LINDEX },
	{ "]",RINDEX },{ "{",LDA },{ "}",RDA },{ "/*",LCOMM },{ "*/",RCOMM }
};


void InitScan(char *filename)
{
	source = fopen(filename, "r");
	if (source == NULL)
	{
		printf("open file %s failed\n", filename);
		exit(1);
	}
}

static tokentype reservedlookup(char *s)
{
	int i;
	for (i = 0; i<ALLTOKEN;i++)
	{
		if (!strcmp(s,reserved_words[i].str))
		return reserved_words[i].tok;
	}
	return ID;
}

static char getChar(void) {
	if (!(linepos < bufsize))
	{
		lineno++;
		if (fgets(lineBuf, BUFLEN - 1, source))
		{
			if (EchoSource) { printf("%4d: %s", lineno, lineBuf); }
			bufsize = strlen(lineBuf);
			linepos = 0;
			return lineBuf[linepos++];
		}
		else {
			EOF_flag = TRUE;
			return EOF;
		}
	}
	else return lineBuf[linepos++];
}

static void ungetChar(void)
{
	if (!EOF_flag)linepos--;
}

void PrintToken(tokentype token, const char *tokenString)
{
	int i;
	switch (token)
	{
	case ID:
		printf("ID,name=%s\n", tokenString);
		break;
	case NUM:
		printf("NUM,value=%s\n", tokenString);
		break;
	case ERROR:
		printf("ERROR:%s\n", tokenString);
		break;
	case ENDFILE:
		printf("EOF\n"); break;
	default:
		for (i = 0; i < ALLTOKEN; i++)
		{
			if (token == reserved_words[i].tok)
			{
				if (i < 6)
				{
					fprintf(stdout, "reserved word:%s\n", tokenString);
					break;
				}
				else
				{
					printf("%s\n", reserved_words[i].str);
					break;
				}
			}
		}
		break;
	}

}

tokentype getToken(void)
{
	int str_index = 0;
	tokentype cur_token = ID;
	statetype state = START;
	int save;
	char pre_char;
	while (state != DONE)
	{
		char c = getChar();
		save = TRUE;
		switch (state)
		{
		case START:
			if ((c == ' ') || (c == '\t') || (c == '\n'))
			{
				save = FALSE;
				state = START;
			}
			else if (isdigit(c))
				state = INNUM;
			else if (isalpha(c))
				state = INID;
			else if ((c == '<') || (c == '>') || (c == '=') || (c == '!'))
				state = IN1;
			else if (c == '/')
			{
				state = IN2;
				save = FALSE;
			}
			else
			{
				state = DONE;
				switch (c) {
				case EOF:
					save = 0;
					cur_token = ENDFILE;
					break;
				case '+':
					cur_token = PLUS;
					break;
				case '-':
					cur_token = MINUS;
					break;
				case '*':
					cur_token = TIMES;
					break;
				case '(':
					cur_token = LKUO;
					break;
				case ')':
					cur_token = RKUO;
					break;
				case '[':
					cur_token = LINDEX;
					break;
				case ']':
					cur_token = RINDEX;
					break;
				case '{':
					cur_token = LDA;
					break;
				case '}':
					cur_token = RDA;
					break;
				case ';':
					cur_token = SMIT;
					break;
				case ',':
					cur_token = COMMA;
					break;
				default:
					cur_token = ERROR;
					break;
				}
			}
			break;
		case INNUM:
			if (!isdigit(c))
			{
				ungetChar();
				save = FALSE;
				state = DONE;
				cur_token = NUM;
			}
			break;
		case INID:
			if (!isalpha(c))
			{
				ungetChar();
				save = FALSE;
				state = DONE;
				cur_token = ID;
			}
			break;
		case IN1:
			state = DONE;
			//UngetChar(); 
			 pre_char = tokenstr[str_index - 1];
			if (c == '=')
			{
				if (pre_char == '<')
					cur_token = LTEQ;
				else if (pre_char == '>')
					cur_token = GTEQ;
				else if (pre_char == '=')
					cur_token = EQ;
				else
					cur_token = NEQ;
			}
			else
				if (pre_char == '<')
					cur_token = LT;
				else if (pre_char == '>')
					cur_token = GT;
				else if (pre_char == '=')
					cur_token = ASSIGN;
				else
					cur_token = ERROR;
			break;
		case IN2: 
			if (c == '*')
			{
				state = INCOMMENT;
				save = FALSE;
			}
			else
				state = DONE; 
			break;
		case INCOMMENT:
			if (c == '*')
			{
				state = IN3;
				save = FALSE;
			}
			else
				state = INCOMMENT;
			    save = FALSE;
			break;
		case IN3:
			if (c == '*')
			{
				state = IN3;
				save = FALSE;
			}
			else if (c == '/')
			{
				state = START ;
				save = FALSE;
			}
			else
				state = INCOMMENT;
			save = FALSE;
			break;
		case DONE:
		default:
			state = DONE;
			cur_token = ERROR;
			break;
		}
		if ((save) && (str_index<=MAXSTR)){
			tokenstr[str_index++] = c;
		}
			if (state == DONE)
			{
				tokenstr[str_index] = '\0';
				if (cur_token == ID)
				{
					cur_token = reservedlookup(tokenstr);
				}
				printf("\t%d:", lineno);

				PrintToken(cur_token, tokenstr);
				str_index = 0;

			}
	}
	return cur_token;
}
